<?php

/**
 * 微信服务商V3
 */
namespace app\api\controller\v3;


use app\Request;
use crmeb\services\app\MiniProgramService;
use crmeb\services\pay\Pay;
use think\facade\Db;
use app\services\car\BaseLogServices;
use GuzzleHttp\Exception\RequestException;
use WechatPay\GuzzleMiddleware\WechatPayMiddleware;
use WechatPay\GuzzleMiddleware\Util\PemUtil;
use WechatPay\GuzzleMiddleware\Util\MediaUtil;
use WechatPay\GuzzleMiddleware\Util\SensitiveInfoCrypto;
use WechatPay\GuzzleMiddleware\Auth\Signer;
use WechatPay\GuzzleMiddleware\Auth\SignatureResult;
use WechatPay\GuzzleMiddleware\Auth\WechatPay2Credentials;
use WechatPay\GuzzleMiddleware\Validator;
use GuzzleHttp\HandlerStack;
use Exception;
use think\log;

class WxController
{
    // 错误信息
    private $error = '';
    // 商户mchid
    private $mch_id;
    // 商户API v3密钥(微信服务商-账户中心-API安全 api v3密钥 https://pay.weixin.qq.com/index.php/core/cert/api_cert)
    private $mch_api_key;
    // 证书编号 (apiclient_cert.pem证书解析后获得)
    private $serial_no ;
    // 私钥 apiclient_key.pem(微信服务商-账户中心-API安全 自行下载 https://pay.weixin.qq.com/index.php/core/cert/api_cert)
//    private $mch_private_key = __DIR__ . DIRECTORY_SEPARATOR . 'cert' . DIRECTORY_SEPARATOR . 'apiclient_key.pem';
    private $mch_private_key;
    // 支付平台公钥(接口获取)
//    private $public_key_path = __DIR__ . DIRECTORY_SEPARATOR . 'cert_ficates_v3.pem';
    private $public_key_path;
    //初始化
    public function __construct()
    {
        $this->mch_id = sys_config('pay_weixin_mchid_v3');
        $this->mch_api_key = sys_config('pay_weixin_key_v3');
        $this->serial_no = sys_config('pay_weixin_serial_no');
        $this->mch_private_key = public_path() . getPemPath(sys_config('pay_weixin_client_cert_v3'));
        //平台证书
        $this->public_key_path = public_path()."/uploads/wx/pt.pem";
    }

    //获取用户openid
    public function get_openid(){
        $appid = 'wxd40250e8dfabab46';
        $secret = "c6a28b6f937e7e38ef5b12c1f152b436";
        $code = input('code','');
        $url = 'https://api.weixin.qq.com/sns/jscode2session?appid=' . $appid . '&secret=' . $secret . '&js_code=' . $code . '&grant_type=authorization_code';

        $curl = curl_init();
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_TIMEOUT, 500);
        // To ensure communication security,all api use https,so need use two line code to open ssl safety check
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($curl, CURLOPT_URL, $url);

        $res = curl_exec($curl);
        curl_close($curl);

        $json_obj = json_decode($res, true);
        $openid = $json_obj["openid"];
        $data['openid'] = $openid;
        return json_encode($data);

    }

    /*
     * 微信特约商户进件接口
     */
    public function subApplyment()
    {
        $params = input('');

        // 参数准备
        $data = [
            //业务申请编号
            'business_code'     => $this->getBusinessCode(),
            //超级管理员信息
            'contact_info'      => [
                'contact_type'=>"SUPER",
                'contact_name'      => $this->getEncrypt($params['contact_name']),//超级管理员姓名
                'contact_id_number' => $this->getEncrypt($params['contact_id_number']),//超级管理员身份证件号码
                'mobile_phone'      => $this->getEncrypt($params['mobile_phone']),//联系手机
                'contact_email'     => $this->getEncrypt($params['contact_email']),//联系邮箱
            ],
            //主体资料
            'subject_info'      => [
                //主体类型SUBJECT_TYPE_INDIVIDUAL（个体户）SUBJECT_TYPE_ENTERPRISE（企业）SUBJECT_TYPE_INSTITUTIONS（党政、机关及事业单位）SUBJECT_TYPE_OTHERS（其他组织）
                'subject_type'  => 'SUBJECT_TYPE_INDIVIDUAL',
                //经营者/法人身份证件
                'identity_info' => [
                    'id_doc_type'  => 'IDENTIFICATION_TYPE_IDCARD',//证件类型
                    'owner'        => true,//经营者/法人是否为受益人
                    //身份证信息
                    'id_card_info' => [
                        'id_card_copy'      => 'jTpGmxUX3FBWVQ5NJTZvlKX_gdU4cRz7z5NxpnFuAxhBTEO_PvWkfSCJ3zVIn001D8daLC-ehEuo0BJqRTvDujqhThn4ReFxikqJ5YW6zFQ',
                        'id_card_national'  => '47ZC6GC-vnrbEny__Ie_An5-tCpqxucuxi-vByf3Gjm7KE53JXvGy9tqZm2XAUf-4KGprrKhpVBDIUv0OF4wFNIO4kqg05InE4d2I6_H7I4',
                        'id_card_name'      => $this->getEncrypt($params['id_card_name']),
                        'id_card_number'    => $this->getEncrypt($params['id_card_number']),
                        'card_period_begin' => '2026-06-06',
                        'card_period_end'   => '2036-06-06',
                    ],
                ],
            ],
            //经营资料
            'business_info'     => [
                'merchant_shortname' => $params['merchant_shortname'],
                'service_phone'      => $params['service_phone'],
                'sales_info'         => [
                    'sales_scenes_type' => ['SALES_SCENES_STORE'],
                    //线下门店场景
                    'biz_store_info'    => [
                        'biz_store_name'     => $params['biz_store_name'],
                        'biz_address_code'   => $params['biz_address_code'],
                        'biz_store_address'  => $params['biz_store_address'],
                        'store_entrance_pic' => ['0P3ng6KTIW4-QJBZR9FwczhJehHhAZN6BKXQPcs-VvdSo'],
                        'indoor_pic'         => ['0P3ng6KTIW4-Q_l2FjmFJBZR9FwczhJehHhAZN6BKXQPcs-VvdSo'],
                    ],
                ],
            ],
            //结算规则
            'settlement_info'   => [
                'settlement_id'      => $params['settlement_id'],
                'qualification_type' => $params['qualification_type'],
                'activities_id'      => $params['activities_id'],
                'activities_rate'    => $params['activities_rate'],
            ],
            //结算银行账户
            'bank_account_info' => [
                'bank_account_type' => 'BANK_ACCOUNT_TYPE_CORPORATE',
                'account_name'      => $this->getEncrypt($params['account_name']),
                'account_bank'      => $params['account_bank'],
                'bank_address_code' => $params['bank_address_code'],
                'bank_name'         => $params['bank_name'],
                'account_number'    => $this->getEncrypt($params['account_number']),
            ],
            //补充材料
            'addition_info'=>[
                'legal_person_commitment'=>$params['legal_person_commitment'],
                'business_addition_pics'=>$params['business_addition_pics'],
            ],
        ];

        $url = 'https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/';

        // 获取支付平台证书编码(也可以用接口中返回的serial_no  来源：https://api.mch.weixin.qq.com/v3/certificates)
        $serial_no = $this->parseSerialNo($this->getCertFicates());
        $bodyData  = json_encode($data);

        // 获取认证信息
        $authorization = $this->getAuthorization($url, 'POST', $bodyData);

        $header = [
            'Content-Type:application/json',
            'Accept:application/json',
            'User-Agent:*/*',

            'Authorization:' . $authorization,
            'Wechatpay-Serial:' . $serial_no
        ];
        $json   = $this->getCurl('POST', $url, $bodyData, $header);

        $data   = json_decode($json, true);

        if (isset($data['code']) && isset($data['message'])) {
            $this->error = '[subApplyment]请求错误 code：' . $data['code'] . ' msg：' . $data['message'];
            return false;
        }
        if (empty($applyment_id = $data['applyment_id'])) {
            $this->error = '[subApplyment]返回错误';
            return false;
        }
        return $applyment_id;
    }

    /**
     * 进件查询
     */
    public function queryApplyment($business_code)
    {
        $url = 'https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/business_code/' . $business_code;

        // 获取认证信息
        $authorization = $this->getAuthorization($url);
        $header        = [
            'Content-Type:application/json',
            'Accept:application/json',
            'User-Agent:*/*',

            'Authorization:' . $authorization
        ];
        $json          = $this->getCurl('GET', $url, '', $header);

        $data = json_decode($json, true);

        if (isset($data['code']) && isset($data['message'])) {
            $this->error = '[queryApplyment]请求错误 code：' . $data['code'] . ' msg：' . $data['message'];
            return false;
        }
        return $data;
    }

    /**
     * 上传文件
     */
    public function mediaUpload()
    {
        // 上传图片
        $filename = '1.png';
        $filepath = __DIR__ . 'WxController.php/' . $filename;
        if (!file_exists($filepath)) {
            $this->error = '[mediaUpload]文件找不到';
        }

        $url       = 'https://api.mch.weixin.qq.com/v3/merchant/media/upload';
        $fi        = new \finfo(FILEINFO_MIME_TYPE);
        $mime_type = $fi->file($filepath);
        $meta      = [
            'filename' => $filename,
            'sha256'   => hash_file('sha256', $filepath)
        ];

        // 获取认证信息
        $authorization = $this->getAuthorization($url, 'POST', json_encode($meta));
        $boundary      = uniqid();
        $header        = [
            'Accept:application/json',
            'User-Agent:*/*',

            'Content-Type:multipart/form-data;boundary=' . $boundary,
            'Authorization:' . $authorization
        ];

        // 组合参数
        $boundaryStr = "--{$boundary}\r\n";
        $out         = $boundaryStr;
        $out         .= 'Content-Disposition: form-data; name="meta"' . "\r\n";
        $out         .= 'Content-Type: application/json' . "\r\n";
        $out         .= "\r\n";
        $out         .= json_encode($meta) . "\r\n";
        $out         .= $boundaryStr;
        $out         .= 'Content-Disposition: form-data; name="file"; filename="' . $filename . '"' . "\r\n";
        $out         .= 'Content-Type: ' . $mime_type . ';' . "\r\n";
        $out         .= "\r\n";
        $out         .= file_get_contents($filepath) . "\r\n";
        $out         .= "--{$boundary}--\r\n";
        $json        = $this->getCurl('POST', $url, $out, $header);
        $data        = json_decode($json, true);

        if (isset($data['code']) && isset($data['message'])) {
            $this->error = '[mediaUpload]请求错误 code：' . $data['code'] . ' msg：' . $data['message'];
            return false;
        }
        if (empty($media_id = $data['media_id'])) {
            $this->error = '[mediaUpload]返回错误';
            return false;
        }
        return $media_id;
    }

    /**
     * 获取微信支付平台证书
     */
    public function certFicates()
    {
        $url = 'https://api.mch.weixin.qq.com/v3/certificates';
        // 获取认证信息
        $authorization = $this->getAuthorization($url);
        $header        = [
            'Content-Type:application/json',
            'Accept:application/json',
            'User-Agent:*/*',

            'Authorization:' . $authorization
        ];
        $json = $this->getCurl('GET', $url, '', $header);
        $data = json_decode($json, true);
        if (isset($data['code']) && isset($data['message'])) {
            $this->error = '[certFicates]请求错误 code：' . $data['code'] . ' msg：' . $data['message'];
            return false;
        }
        if (empty($cfdata = $data['data'][0])) {
            $this->error = '[certFicates]返回错误';
            return false;
        }
        return $cfdata;
    }

    /**
     * 获取认证信息
     * @param string $url
     * @param string $http_method
     * @param string $body
     * @return string
     * @throws Exception
     */
    public function getAuthorization($url, $http_method = 'GET', $body = '')
    {
        if (!in_array('sha256WithRSAEncryption', \openssl_get_md_methods(true))) {
            throw new \Exception("当前PHP环境不支持SHA256withRSA");
        }
        //私钥地址
        $mch_private_key = $this->mch_private_key;
        //商户号
        $merchant_id = $this->mch_id;
        //当前时间戳
        $timestamp = time();
        //随机字符串
        $nonce = buildNonceStr();
        //证书编号
        $serial_no = $this->serial_no;
        $url_parts     = parse_url($url);
        $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
        $message       = $http_method . "\n" .
            $canonical_url . "\n" .
            $timestamp . "\n" .
            $nonce . "\n" .
            $body . "\n";
        openssl_sign($message, $raw_sign, \openssl_get_privatekey(\file_get_contents($mch_private_key)), 'sha256WithRSAEncryption');
        $sign = base64_encode($raw_sign);

        $schema = 'WECHATPAY2-SHA256-RSA2048';
        $token  = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
            $merchant_id, $nonce, $timestamp, $serial_no, $sign);
        return $schema . ' ' . $token;
    }


    public function tt(){
        $data = "Beeeeer is really good.. hic...";
        $private_key = <<<EOD
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDkHMJ6ktPZ4iRM
0uFYmJ6iHTb/vRPznOW3qOUW4Nj4IJtq6Pxg6uMePEzbGdIhJDj1BOgVaH8w/ZTz
L08uAo1Em7MNopTEFB/g9NVOrKl9WGwVBnCnas0ujaZWbvU+h9mbJyAkD6N6wYOo
bTq9B/vTqHI2TBC4NuybxK6ZKoVsVfPL2EB4/mAJF5mvmfssbmjNZ5hE2mqb4fIR
yirMaM/BIJgOeL2D7Euf+eSZKtlYrBZcK7/x2+kDKqFbJ4LveVVsekrRPmq8X3jX
nS7epeR6L9megxXQCH5aeIA51g3Xl9V3Qc0DW/CI0yu4XMggwGIT4Yo20T0p0ahq
KSTIi4idAgMBAAECggEABauFVgCiTW8mkdZIDai1gLgTLuYnQc3rk9YZ46ixG/HL
iyvBYjNUc+Lx0hxhMbuxwr1de3K+fpw/AJBzemRL2KG6IKOi6QDLDg9n4e2+xMPW
4+rmD/NCqF6S/OEDLYu45/dgACV3XczDlgMpC5vQMaGlPR5E6B9NYE3SPTJmYJvy
S/QiSvmVJTqYNjpOHq0qHWi0U3Ww8y2FfNHp29Tg6qXQMRaJQc9V+wRTweZDYFvQ
Bl9JiZNrDB5McNYM0Cg+UnvSGML0M+w2YoRbXLcO6Fy6S2npx65XT0JaGV9oaJj8
T5HB49vHkFJQGQesbDsq000KQ9hspYBjUSoik0N+AQKBgQD/20/k0/ud3k1CwwoQ
adDsThUa1qRFiqa7XrHzYJhJ3X2m2IqaqbfQtyeOPNVG7+t02YmLgBFgMkH3nLfM
s2F3I6O4oH71hvFxxtNf/fefr0LvQfAfsb2uAsVdlBMxZ5O9nB7e6POa0YtdReLm
v1ujAdeY9lrNH7D5sTKIilBOgQKBgQDkPXgh8+ujZSJurpAtPP3aYkcPPc5cP4RX
fZmaT0EVSTLrGOZhqJvrN6nT46N5y7pxFBtiCjw22xG/uVSUaiE42MRAENaQko4y
Aq9s9fy/JdD/FVoKxNqb6rCQii4Sp222pipmsuWEsDph9WtZ7kQMyXS3cCDer5pY
rHov5yikHQKBgCV5et86SU1bJRfVN2EmDoq9+QtgStlAsKEbApdyG3weK+xHJS34
E8tHOEz2DgsA0wzfW+0JygG7+ruIwccCsxDfMHi3UUEnDbArfgQ+T4mdAwHGN6R8
xRp6n2vQZ2tCD3qjJkZh+mQ/stjrhotjlclvWKO32Z7XuHUqBgvHrnGBAoGAZu6h
4w4SUxolMS1gsEY1Zeu0iFcjXc73bE2knEbp3yOn5wbPv3M+/hGYcYrUCvWqG50p
zEk5ZrPdiU2Xq2dBnFWGDC6L1Z+CqWM0ojs9xSbU3nVOqlSlEnQBNO5cYOVJQXom
3TII+JJRgiQgSep+nhWsoZ3b9RHU1fDTGg9x8LECgYAiQTay/VRFr7bMvGH+OKgk
dxbLFpYoIq14YZ7ePjuIJVI3INpraubrVSL8dw3Aiaw5oBoemjHbJFePAlJ2E8VS
QvtWfwnbcAGJyD3/pZOb4bfRBKrqd6zzMejLmbVP5L6rgPeAPfQORb5V07Is7qje
2WTtn9tewyAnzMEgDiv/Mg==
-----END PRIVATE KEY-----


EOD;
        $public_key = <<<EOD
-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANDiE2+Xi/WnO+s120NiiJhNyIButVu6
zxqlVzz0wy2j4kQVUC4ZRZD80IY+4wIiX2YxKBZKGnd2TtPkcJ/ljkUCAwEAAQ==
-----END PUBLIC KEY-----
EOD;


//上面是官方的  改造一下
///
//例如我们拿到的公钥私钥是一行的
//私钥
        $str='MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDkHMJ6ktPZ4iRM0uFYmJ6iHTb/vRPznOW3qOUW4Nj4IJtq6Pxg6uMePEzbGdIhJDj1BOgVaH8w/ZTzL08uAo1Em7MNopTEFB/g9NVOrKl9WGwVBnCnas0ujaZWbvU+h9mbJyAkD6N6wYOobTq9B/vTqHI2TBC4NuybxK6ZKoVsVfPL2EB4/mAJF5mvmfssbmjNZ5hE2mqb4fIRyirMaM/BIJgOeL2D7Euf+eSZKtlYrBZcK7/x2+kDKqFbJ4LveVVsekrRPmq8X3jXnS7epeR6L9megxXQCH5aeIA51g3Xl9V3Qc0DW/CI0yu4XMggwGIT4Yo20T0p0ahqKSTIi4idAgMBAAECggEABauFVgCiTW8mkdZIDai1gLgTLuYnQc3rk9YZ46ixG/HLiyvBYjNUc+Lx0hxhMbuxwr1de3K+fpw/AJBzemRL2KG6IKOi6QDLDg9n4e2+xMPW4+rmD/NCqF6S/OEDLYu45/dgACV3XczDlgMpC5vQMaGlPR5E6B9NYE3SPTJmYJvyS/QiSvmVJTqYNjpOHq0qHWi0U3Ww8y2FfNHp29Tg6qXQMRaJQc9V+wRTweZDYFvQBl9JiZNrDB5McNYM0Cg+UnvSGML0M+w2YoRbXLcO6Fy6S2npx65XT0JaGV9oaJj8T5HB49vHkFJQGQesbDsq000KQ9hspYBjUSoik0N+AQKBgQD/20/k0/ud3k1CwwoQadDsThUa1qRFiqa7XrHzYJhJ3X2m2IqaqbfQtyeOPNVG7+t02YmLgBFgMkH3nLfMs2F3I6O4oH71hvFxxtNf/fefr0LvQfAfsb2uAsVdlBMxZ5O9nB7e6POa0YtdReLmv1ujAdeY9lrNH7D5sTKIilBOgQKBgQDkPXgh8+ujZSJurpAtPP3aYkcPPc5cP4RXfZmaT0EVSTLrGOZhqJvrN6nT46N5y7pxFBtiCjw22xG/uVSUaiE42MRAENaQko4yAq9s9fy/JdD/FVoKxNqb6rCQii4Sp222pipmsuWEsDph9WtZ7kQMyXS3cCDer5pYrHov5yikHQKBgCV5et86SU1bJRfVN2EmDoq9+QtgStlAsKEbApdyG3weK+xHJS34E8tHOEz2DgsA0wzfW+0JygG7+ruIwccCsxDfMHi3UUEnDbArfgQ+T4mdAwHGN6R8xRp6n2vQZ2tCD3qjJkZh+mQ/stjrhotjlclvWKO32Z7XuHUqBgvHrnGBAoGAZu6h4w4SUxolMS1gsEY1Zeu0iFcjXc73bE2knEbp3yOn5wbPv3M+/hGYcYrUCvWqG50pzEk5ZrPdiU2Xq2dBnFWGDC6L1Z+CqWM0ojs9xSbU3nVOqlSlEnQBNO5cYOVJQXom3TII+JJRgiQgSep+nhWsoZ3b9RHU1fDTGg9x8LECgYAiQTay/VRFr7bMvGH+OKgkdxbLFpYoIq14YZ7ePjuIJVI3INpraubrVSL8dw3Aiaw5oBoemjHbJFePAlJ2E8VSQvtWfwnbcAGJyD3/pZOb4bfRBKrqd6zzMejLmbVP5L6rgPeAPfQORb5V07Is7qje2WTtn9tewyAnzMEgDiv/Mg==';
        $str        = chunk_split($str, 64, "\n");

        $private_key = "-----BEGIN RSA PRIVATE KEY-----\n$str-----END RSA PRIVATE KEY-----\n";

//公钥
        $str='MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANDiE2+Xi/WnO+s120NiiJhNyIButVu6zxqlVzz0wy2j4kQVUC4ZRZD80IY+4wIiX2YxKBZKGnd2TtPkcJ/ljkUCAwEAAQ==';
        $str               = chunk_split($str, 64, "\n");
        $public_key = "-----BEGIN PUBLIC KEY-----\n$str-----END PUBLIC KEY-----\n";

        $binary_signature = "";

        openssl_sign($data, $binary_signature, $private_key, OPENSSL_ALGO_SHA1);

// Check signature
        $ok = openssl_verify($data, $binary_signature, $public_key, OPENSSL_ALGO_SHA1);

        echo "check #1: ";
        if ($ok == 1) {
            echo "signature ok (as it should be)\n";
        } elseif ($ok == 0) {
            echo "bad (there's something wrong)\n";
        } else {
            echo "ugly, error checking signature\n";
        }

        $ok = openssl_verify('tampered'.$data, $binary_signature, $public_key, OPENSSL_ALGO_SHA1);
        echo "check #2: ";
        if ($ok == 1) {
            echo "ERROR: Data has been tampered, but signature is still valid! Argh!\n";
        } elseif ($ok == 0) {
            echo "bad signature (as it should be, since data has beent tampered)\n";
        } else {
            echo "ugly, error checking signature\n";
        }
    }


    /**
     * 敏感字符加密
     * @param $str
     * @return string
     * @throws Exception
     */
    public function getEncrypt($str)
    {
        static $content;
        if (empty($content)) {
            $content = $this->getCertFicates();
        }
        $encrypted = '';
        if (openssl_public_encrypt($str, $encrypted, $content, OPENSSL_PKCS1_OAEP_PADDING)) {
            //base64编码
            $sign = base64_encode($encrypted);
        }
        else {
            throw new \Exception('encrypt failed');
        }
        return $sign;
    }

    /**
     * 获取支付平台证书
     * @return false|string
     */
    public function getCertFicates()
    {
        $public_key_path = $this->public_key_path;
        if (!file_exists($public_key_path)) {
            $cfData = $this->certFicates();
            $content = $this->decryptToString($cfData['encrypt_certificate']['associated_data'], $cfData['encrypt_certificate']['nonce'], $cfData['encrypt_certificate']['ciphertext'], $this->mch_api_key);
            file_put_contents($public_key_path, $content);
        }
        else {
            $content = file_get_contents($public_key_path);
        }
        return $content;
    }

    /**
     * 业务编号
     * @return string
     */
    private function getBusinessCode()
    {
        return date('Ymd') . substr(time(), -5) . substr(microtime(), 2, 5) . sprintf('%02d', rand(0, 99));
    }


    /**
     * 获取商户私钥
     */
    public function getPrivateKey()
    {
        if (is_file($this->mch_private_key)) {
            $this->mch_private_key = openssl_pkey_get_private(file_get_contents($this->mch_private_key));
        }
    }

    /**
     * @param string       $method
     * @param string       $url
     * @param array|string $data
     * @param array        $headers
     * @param int          $timeout
     * @return bool|string
     */
    public function getCurl($method = 'GET', $url, $data, $headers = [], $timeout = 10)
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_HEADER, false);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);

        if (!empty($headers)) {
            curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        }
        if ($method == 'POST') {
            curl_setopt($curl, CURLOPT_POST, TRUE);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        }
        else {
        }
        $result = curl_exec($curl);

        curl_close($curl);
        return $result;
    }

    /**
     * Decrypt AEAD_AES_256_GCM ciphertext(官方案例-已改造)
     *
     * @param string $associatedData AES GCM additional authentication data
     * @param string $nonceStr       AES GCM nonce
     * @param string $ciphertext     AES GCM cipher text
     *
     * @return string|bool      Decrypted string on success or FALSE on failure
     */
    private function decryptToString($associatedData, $nonceStr, $ciphertext, $aesKey)
    {
        $auth_tag_length_byte = 16;
        $ciphertext = \base64_decode($ciphertext);
        if (strlen($ciphertext) <= $auth_tag_length_byte) {
            return false;
        }

        // ext-sodium (default installed on >= PHP 7.2)
        if (function_exists('\sodium_crypto_aead_aes256gcm_is_available') &&
            \sodium_crypto_aead_aes256gcm_is_available()) {
            return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $aesKey);
        }

        // ext-libsodium (need install libsodium-php 1.x via pecl)
        if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') &&
            \Sodium\crypto_aead_aes256gcm_is_available()) {
            return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $aesKey);
        }

        // openssl (PHP >= 7.1 support AEAD)
        if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) {
            $ctext   = substr($ciphertext, 0, -$auth_tag_length_byte);
            $authTag = substr($ciphertext, -$auth_tag_length_byte);

            return \openssl_decrypt($ctext, 'aes-256-gcm', $aesKey, \OPENSSL_RAW_DATA, $nonceStr,
                $authTag, $associatedData);
        }

        throw new \Exception('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php');
    }

    /**
     * 获取证书编号(官方案例-已改造)
     * @param $certificate
     * @return string
     */
    public function parseSerialNo($certificate)
    {
        $info = \openssl_x509_parse($certificate);
        if (!isset($info['serialNumber']) && !isset($info['serialNumberHex'])) {
            throw new \InvalidArgumentException('证书格式错误');
        }

        $serialNo = '';
        // PHP 7.0+ provides serialNumberHex field
        if (isset($info['serialNumberHex'])) {
            $serialNo = $info['serialNumberHex'];
        }
        else {
            // PHP use i2s_ASN1_INTEGER in openssl to convert serial number to string,
            // i2s_ASN1_INTEGER may produce decimal or hexadecimal format,
            // depending on the version of openssl and length of data.
            if (\strtolower(\substr($info['serialNumber'], 0, 2)) == '0x') { // HEX format
                $serialNo = \substr($info['serialNumber'], 2);
            }
            else { // DEC format
                $value     = $info['serialNumber'];
                $hexvalues = ['0', '1', '2', '3', '4', '5', '6', '7',
                    '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
                while ($value != '0') {
                    $serialNo = $hexvalues[\bcmod($value, '16')] . $serialNo;
                    $value    = \bcdiv($value, '16', 0);
                }
            }
        }

        return \strtoupper($serialNo);
    }

    public function getError()
    {
        return $this->error;
    }





}
